# -*- coding: utf-8 -*-

# @File   : data.py
# @Author : Yuvv
# @Date   : 2017/11/11

import logging

import mongoengine as me
from bson.objectid import ObjectId
from mongoengine.errors import ValidationError


class DataContent(me.DynamicDocument):
    """数据内容类，具体字段不确定"""

    _meta_id = me.IntField(min_value=0, required=True, db_field='_meta_id')
    _tid = me.IntField(min_value=0, required=True, db_field='_tid')

    meta = {
        'indexes': [
            '_meta_id',
            '_tid',
        ],
    }

    def delete(self, *args, **kwargs):
        """删除同时检查各个字段，删除各字段内容"""
        for field_cls in (DataFieldTable, DataFieldContainer):
            for field in field_cls.objects(_meta_id=self._meta_id, _owner_id=self.pk):
                field.delete()
        super(DataContent, self).delete(*args, **kwargs)

    @staticmethod
    def add(meta_id, tid, content):
        """
        添加新的数据内容并返回
        :param meta_id: 元数据 id
        :param tid: 模板 id
        :param content: 数据内容
        :type content: dict
        :return: 返回新添加的数据内容对象
        :rtype: DataContent
        """
        dc_id = ObjectId()
        try:
            if isinstance(content.get('_id'), ObjectId):
                dc_id = content.pop('_id')
            dc = DataContent(**content)
            dc.id = dc_id
            dc._meta_id = meta_id
            dc._tid = tid
            dc.save()
            return dc
        except ValidationError as ex:
            for field_cls in (DataFieldTable, DataFieldContainer):
                for field in field_cls.objects(_owner_id=dc_id):
                    field.delete()
            raise ex


class DataFieldTable(me.Document):
    """表格型数据

    `title`: 表格标题（table 标签的 title 部分，并非字段名）
    `head`:  列标题（表头）
    """
    _meta_id = me.IntField(min_value=0, required=True, db_field='_meta_id')
    _tid = me.IntField(min_value=0, required=True, db_field='_tid')
    _owner_id = me.ObjectIdField(required=True, db_field='_owner_id')
    _path = me.StringField(required=True, db_field='_path')
    title = me.StringField()
    head = me.ListField(field=me.StringField(required=True))

    meta = {
        'indexes': [
            '_meta_id',
            '_tid',
            '_owner_id',
            '_path',
        ],
    }

    def delete(self, *args, **kwargs):
        """删除同时删除行数据"""
        for row in DataFieldTableRow.objects(_meta_id=self._meta_id, _owner_id=self.pk):
            row.delete()
        super(DataFieldTable, self).delete(*args, **kwargs)

    @staticmethod
    def add(meta_id, tid, owner_id, path, title, head, rows=()):
        """
        创建一个表格数据并返回
        :param meta_id: 元数据 id
        :param tid: 模板 id
        :param owner_id: 所属对象的 ObjectId
        :param path: 对象名称路径
        :param title: 表格标题
        :type title: str
        :param head: 表头名称
        :type head: list
        :param owner_id: 所属对象的 ObjectId
        :type owner_id: str
        :param rows: 二维数组或行对象的一维数组，每一行数据，行内只能是字符串或数值（没有做检验）
        :type head: list
        :return: 返回创建的表格
        :rtype: DataFieldTable
        """
        dt = None
        try:
            dt = DataFieldTable(title=title, head=head, _owner_id=owner_id, _path=path, _meta_id=meta_id, _tid=tid)
            dt.save()
            head_len = len(head)
            for row in rows:
                if isinstance(row, (list, tuple)):
                    assert len(row) == head_len
                    DataFieldTableRow.objects.create(_meta_id=meta_id, _tid=tid, _owner_id=dt.id, _path=path,
                                                     **dict(zip(head, row)))
                elif isinstance(row, dict):
                    DataFieldTableRow.add(meta_id, dt.id, path, row)
                else:
                    raise ValidationError
            return dt
        except Exception as ex:
            if dt and dt.pk:
                dt.delete()
            raise ex

    @staticmethod
    def add_rows(table, rows):
        """
        向`table`添加新的行
        :param table: 待添加行数据的表格
        :type table: DataFieldTable
        :param rows: 待添加的行数据
        :param rows: list
        :return: None
        """
        n_rows = list()
        try:
            head_len = len(table.head)
            for row in rows:
                assert len(row) == head_len
                row = DataFieldTableRow.objects.create(_meta_id=table._meta_id, _tid=table._tid,
                                                       _owner_id=table.id, _path=table._path,
                                                       **dict(zip(table.head, row)))
                n_rows.append(row)
        except ValidationError as ex:
            logging.error('Some error occur when save DataFieldTable(%s).\n%s',
                          table.title, str(ex))
            raise ex
        except Exception as ex:
            for row in n_rows:
                row.delete()
            raise ex


class DataFieldTableRow(me.DynamicDocument):
    """表格字段实际存储的每一行的内容"""

    _meta_id = me.IntField(min_value=0, required=True, db_field='_meta_id')
    _tid = me.IntField(min_value=0, required=True, db_field='_tid')
    _owner_id = me.ObjectIdField(required=True, db_field='_owner_id')
    _path = me.StringField(required=True, db_field='_path')

    meta = {
        'indexes': [
            '_meta_id',
            '_tid',
            '_owner_id',
            '_path',
        ],
    }

    @staticmethod
    def add(meta_id, tid, owner_id, path, row):
        """
        添加一行数据
        :param meta_id: 元数据 id
        :param tid: 模板 id
        :param owner_id: 所属对象的 ObjectId
        :param path: 对象名称路径
        :param row: 需要添加的行，需是表头和值对应的 dict（没有做校验）
        :type row: dict
        :return: 返回新创建的行
        :rtype: DataFieldTableRow
        """
        tr = DataFieldTableRow.objects.create(_meta_id=meta_id, _tid=tid,
                                              _owner_id=ObjectId(owner_id), _path=path,
                                              **row)
        return tr


class DataFieldContainer(me.DynamicDocument):
    """容器型数据，内容不确定"""

    _meta_id = me.IntField(min_value=0, required=True, db_field='_meta_id')
    _tid = me.IntField(min_value=0, required=True, db_field='_tid')
    _owner_id = me.ObjectIdField(required=True, db_field='_owner_id')
    _path = me.StringField(required=True, db_field='_path')

    meta = {
        'indexes': [
            '_meta_id',
            '_tid',
            '_owner_id',
            '_path',
        ],
    }

    def delete(self, *args, **kwargs):
        """删除同时检查各个字段，删除各字段内容"""
        for field_cls in (DataFieldTable, DataFieldContainer):
            for field in field_cls.objects(_meta_id=self._meta_id, _owner_id=self.pk):
                field.delete()
        super(DataFieldContainer, self).delete(*args, **kwargs)

    @staticmethod
    def add(meta_id, tid, owner_id, path, content: dict):
        """
        添加新的容器型数据并返回
        :param meta_id: 元数据 id
        :param tid: 模板 id
        :param owner_id: 所属对象的 ObjectId
        :param path: 对象名称路径
        :param content: 待添加的容器型数据
        :type content: dict
        :return: 返回添加的容器型数据
        :rtype: DataFieldContainer
        """
        ctn_id = ObjectId()
        try:
            if isinstance(content.get('_id'), ObjectId):
                ctn_id = content.pop('_id')
            dfc = DataFieldContainer.objects.create(id=ctn_id, _meta_id=meta_id, _tid=tid, _owner_id=owner_id,
                                                    _path=path,
                                                    **content)
            return dfc
        except ValidationError as ex:
            for field_cls in (DataFieldTable, DataFieldContainer):
                for field in field_cls.objects(_owner_id=ctn_id):
                    field.delete()
            raise ex
